package fraclac;

import ij.ImagePlus;
import ij.gui.Roi;
import ij.process.ImageProcessor;
import ij.process.ImageStatistics;
import imageprocessing.DePaper;
import imageprocessing.Util;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.PrintWriter;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import java.util.Map.Entry;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;

import nf.ImagePreprocessor;
import nf.Worker;
import fraclac.FLAnalyzer.FLVars;
import fraclac.FLAnalyzer.FracStats;
import fraclac.FLAnalyzer.StoreAvgs;
import fraclac.FLUtilities.FLstatistics;
import fraclac.FLUtilities.FLutil;

public class FracLacWrapper
{

	private static final char DECIMAL_SEPARATOR = ',';

	public static int cpuCoresOverride = 0;
	public static int maxEntries = 0;

	@SuppressWarnings("unused")
	private static void recursiveImageSearch(ArrayList<String> list, File curDir)
	{
		File[] files = curDir.listFiles();
		for (File file : files)
		{
			if (!file.isDirectory())
			{
				String lowerName = file.getName().toLowerCase();
				if (lowerName.endsWith(".jpg") || lowerName.endsWith(".jpeg")
						|| lowerName.endsWith(".png")
						|| lowerName.endsWith(".bmp")
						|| lowerName.endsWith(".gif"))
				{
					if ("converted.png".equals(lowerName))
						continue;
					if (lowerName.endsWith("-converted.png"))
						continue;
					if ("deforeground-blur.png".equals(lowerName))
						continue;
					if ("deforeground.png".equals(lowerName))
						continue;

					list.add(file.getAbsolutePath());
				}
			}
		}
		for (File file : files)
		{
			if (file.isDirectory())
			{
				recursiveImageSearch(list, file);
			}
		}
	}

	

	/*
	private static int findLast(char c, String txt)
	{
		int pos = -1;
		while (true)
		{
			int npos = txt.indexOf(c, pos + 1);
			if (npos == -1)
				return pos;
			else
				pos = npos;
		}
	}
	*/

	//private static String getParentPath(String fileName)
	//{
	//	return fileName.substring(0, findLast(File.separatorChar, fileName));
	//}

	

	/*
	 * private static void dumpVars(Object o) { Field[] fields =
	 * o.getClass().getFields(); for(Field f : fields) { try { Object value =
	 * f.get(o); String v; if(value == null) v = "[null]"; else v =
	 * value.toString();
	 * 
	 * System.out.println(f.getType().toString() + " " + f.getName() + " = " +
	 * v); } catch (IllegalArgumentException e) { // TODO Auto-generated catch
	 * block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO
	 * Auto-generated catch block e.printStackTrace(); } } System.out.println();
	 * }
	 */

	private static boolean CheckBW(ImagePlus img, FLVars V)
	{
		ImageStatistics stats = img.getStatistics();
		int TotalAllPixels = stats.histogram[255] + stats.histogram[0];
		boolean abort = false;
		if (TotalAllPixels != stats.pixelCount)
		{

			if (!V.thresh)
			{
				V.abort = true;
			} else
			{

				
				//To8(img);
				/*
				Image srcImg = img.getImage();
				BufferedImage buffImg = Util.sameCanvasSize(srcImg);
				Graphics g = buffImg.getGraphics();
				g.drawImage(srcImg, 0, 0, null);
				g.dispose();

				// BufferedImage srcImg = img.getBufferedImage();
				// int thresh = DePaper.findBrightnessHalfpoint(buffImg);
				// img.setImage(DePaper.thresholdBW(buffImg, thresh,
				// false));
				img.setImage(DePaper.autoThreshold(buffImg));
				*/
				

				//Util.saveImage((BufferedImage) img.getBufferedImage(), img.getTitle()
				//		+ "-converted.png");

				stats = img.getStatistics();
				TotalAllPixels = stats.histogram[255] + stats.histogram[0];
				if (TotalAllPixels != stats.pixelCount)
				{
					System.out.println("error " + stats.pixelCount + " vs "
							+ TotalAllPixels);
					for(int i = 0; i <= 255; ++i)
					{
						System.out.println("Histogram[" + i + "] = " + stats.histogram[i]);
					}
					V.abort = true;
					abort = true;
				}
			}
		}

		return abort;
	}

	private static class FLEmu
	{
		//private String sizestring;
		//private String countstring;
		private Rectangle RectangleEnclosingForeground;
		private float[] boxSizeOverMaxWidth;
		private int[] boxSizes;
		private float[][] AvgPixAtLocAndEpsilon;
		private float[][] Lacunarity;
		private float[][] ELacunarity;
		//private float[][] LacunarityCount;
		//private float[][] LacEmptiesCount;
		private float[][] AvgPixPlusEmpties;
		private float[][] AveragePixOverTotalPix;
		private float[][] StdDevInPixelsPerBox;
		private float[][] TestArea;
		private float[] TestAreas;
		private float[][] StdDevPlusEmpties;
		//private int[][] MinArray;
		private float[][] SqrdCVforPixelsPerBox;
		private float[][] ESqrdCVforPixelsPerBox;
		private float[][] binnedPDLAtEpsilon;
		private float[][] binnedMeanOfProbDistributionAtEpsilon;
		private float[][] binnedStdDevOfPDistributionAtEpsilon;
		private float[][] binnedCVofProbDensityAtEpsilon;
		//private int[] ListCounters;
		@SuppressWarnings("rawtypes")
		private ArrayList ListOfMasses;
		//private String[][] Frequencies;
		//private String[][] BigFrequencies;
		//private String[][] Masses;
		private int[][] NumberOfEmptyBoxesAtLocationAndEpsilon;
		private float[][] FullSummedPtoQAtEpsilons;
		private float[][] FullMeanTau;
		private float[][] FullAlphaArray;
		private float[][] FullFArray;
		private int empties;
		private int[] ForegroundPixSampled;
		private int[] TotalPixSampled;
		
		
		
		
		
		
		
		
		private static final String tab = ";"; //"\t";
		private static final String newline = "\n";
		
	}

	private static int[] FindMargins(ImageProcessor ip, FLEmu F, FLVars V)
	{
		int[] histogram = new int[256];
		float totalwidth = ip.getWidth();
		float totalheight = ip.getHeight();
		int[] margins = new int[4];
		V.CVRadii = 0;
		V.MeanRadii = 0;
		V.MaxRadius = 0;
		V.MaxOverMinRadii = 0;
		// Find left edge
		int margleft = -1;
		int margbottom = -1;
		int margright = -1;
		int margtop = -1;
		do// scan the image at its full height, starting from
			// the top left until a pixel is found, then call this
			// the left margin
		{
			margleft++;
			if (margleft >= totalwidth)
			{
				System.out.println("No non-background pixels found.");
				V.setAbort(true);
				return null;
			}
			if (!V.isAbort())
			{
				// IJ.write("got here");
				ip.setRoi((int) margleft, 0, 1, (int) totalheight);
				histogram = ip.getHistogram();
			}
		} while (histogram[V.getForeground()] == 0);

		if (!V.isAbort())
		{
			int w = (int) (totalwidth - margleft);
			if (w < 1)
			{
				System.out.println("too skinny");
				w = 1;
			}
			// Find top edge
			margtop = -1;
			do
			{
				margtop++;
				ip.setRoi((int) margleft, (int) margtop, w, 1);
				histogram = ip.getHistogram();
			} while (histogram[V.getForeground()] == 0);

			// Find right edge
			int h = (int) (totalheight - margtop);
			if (h < 1)
			{
				System.out.println("too skinny");
				h = 1;
			}
			;

			margright = (int) (totalwidth + 1);
			do
			{
				margright--;
				ip.setRoi((int) margright - 1, (int) margtop, 1, h);
				histogram = ip.getHistogram();

			} while (histogram[V.getForeground()] == 0);
			int margWidth = (margright - margleft);
			if (margWidth < 2)
			{
				margright = margleft + 1;
				V.isVerticalLine = true;
				// System.out.println("v");

			} else
				V.isVerticalLine = false;
			margWidth = (margright - margleft);
			// Find bottom edge

			margbottom = (int) (totalheight + 1);
			// int L=(int)(margright-margleft);
			// if (L<1)L=1;
			do
			{
				margbottom--;
				ip.setRoi((int) margleft, (int) margbottom - 1,
						(int) margWidth, 1);
				histogram = ip.getHistogram();
			} while (histogram[V.getForeground()] == 0);

			int margHeight = (margbottom - margtop);
			// System.out.println("k" +margHeight);
			if (margHeight < 2)
			{
				V.isHorizontalLine = true;
				margbottom = margtop + 1;

				// System.out.println("H");

			} else
				V.isHorizontalLine = false;
			margHeight = (margbottom - margtop);

			F.RectangleEnclosingForeground = new Rectangle((int) margleft,
					(int) margtop, (int) margWidth, (int) margHeight);
			V.LesserOfRectangleHeightAndWidth = Math.min(
					F.RectangleEnclosingForeground.width,
					F.RectangleEnclosingForeground.height);

			V.GreaterOfRectangleHeightAndWidth = (float) Math.max(
					F.RectangleEnclosingForeground.width,
					F.RectangleEnclosingForeground.height);
			if (V.GreaterOfRectangleHeightAndWidth == 0)
				System.out.println("Ah");

			V.GreaterHorizontalOrVerticalDimension = V.GreaterOfRectangleHeightAndWidth;
		}

		margins[0] = margleft;
		margins[1] = margright;
		margins[2] = margtop;
		margins[3] = margbottom;

		return margins;
	}

	private @SuppressWarnings({ "unchecked", "rawtypes" })
	static void initializeFreqArray(int n, FLEmu F)
	{

		F.ListOfMasses = new ArrayList(n);

		for (int i = 0; i < n; i++)
		{
			try
			{
				F.ListOfMasses.add(i, new ArrayList());
			} catch (UnsupportedOperationException un)
			{
			} catch (ClassCastException cc)
			{
			} catch (IllegalArgumentException g)
			{
			}

			catch (NullPointerException hg)
			{
			} finally
			{
			}
		}

	}

	private static int[] MakeScaledSeries(float LargestSize, float Scale,
			float SmallestBox)
	{
		int PossibleBoxes = 1 + (int) (Math.log(LargestSize) / Math.log(Scale));

		int N = 0;
		float size = LargestSize;
		for (int i = 0; i < PossibleBoxes; i++)
		{
			float newsize = (size / Scale);
			int check = Math.round(newsize);
			if (check < Math.round(size) && check >= (int) SmallestBox)
			{
				size = newsize;
				N++;
			}
		}
		int count = 1;
		float LastSize = LargestSize;
		int[] IntSizes = new int[N + 1];
		IntSizes[0] = (int) LargestSize;
		for (int i = 1; i < PossibleBoxes; i++)
		{
			float newsize = (LastSize / Scale);
			int check = Math.round(newsize);
			if (check < Math.round(LastSize) && check >= (int) SmallestBox)
			{
				IntSizes[count] = check;
				LastSize = newsize;
				count++;
			}
		}
		int[] SmallestToLargest = new int[count];
		int c = 0;
		for (int i = count - 1; i >= 0; i--)
		{
			SmallestToLargest[c] = IntSizes[i];
			c++;
		}
		return SmallestToLargest;
	}

	private static int MakeNewBoxSizes(float Across, boolean issub, FLEmu F,
			FLVars V)
	{
		// set the upper limit on box size

		V.limit = (int) (Across * (V.maxpercent / 100.0f));
		if (V.UseGreater)
			V.limit = (int) (V.GreaterOfRectangleHeightAndWidth * (V.maxpercent / 100f));
		// set the limit to a percentage of the roi
		if (issub)
			V.limit = (int) (Across * (V.maxsubpercent / 100.0f));
		if (issub && V.maxsubpercent <= 0)
			V.limit = (int) V.Subboxsize;

		if ((V.MF) && (!issub))
		{
			if ((V.MFUserMaxBoxSize > 0))
				V.limit = V.MFUserMaxBoxSize;
			else
				V.limit = (int) (V.UseGreater ? ((V.GreaterOfRectangleHeightAndWidth + 1f) * (V.maxpercent / 100.0f))
						: (V.LesserOfRectangleHeightAndWidth * (V.maxpercent / 100.0f)));
			if (V.isVerticalLine || V.isHorizontalLine)
				if (V.MFUserMaxBoxSize <= 0)
					V.limit = (int) (V.GreaterOfRectangleHeightAndWidth * (V.maxpercent / 100.0f));
		} else if ((V.SLAC) && (!issub))
		{
			if ((V.LacUserMaxBoxSize > 0))
				V.limit = V.LacUserMaxBoxSize;
			else
				V.limit = (int) ((V.maxpercent / 100f) * (V.UseGreater ? V.GreaterOfRectangleHeightAndWidth
						: V.LesserOfRectangleHeightAndWidth));
			if (V.isVerticalLine || V.isHorizontalLine)
				if (V.SLAC && V.LacUserMaxBoxSize <= 0)
					V.limit = (int) (V.GreaterOfRectangleHeightAndWidth * (V.maxpercent / 100.0f));
		} else if (!issub)
		{
			if ((V.BoxCountUserMaxBoxSize > 0) && (!issub))
				V.limit = V.BoxCountUserMaxBoxSize;
			if (V.isVerticalLine || V.isHorizontalLine)
				if (V.BoxCountUserMaxBoxSize == 0)
					V.limit = (int) (V.GreaterOfRectangleHeightAndWidth * (V.maxpercent / 100.0f));
		}

		if (V.limit < 1)
			V.limit = 1;
		int MaxBoxSize = V.limit;

		int numboxes = MaxBoxSize;
		if (V.MF)
		{
			if (V.MFuserNumber)
				numboxes = V.MaximumMFBoxSizes;
		} else if (V.SLAC)
		{
			if (V.LacuserNumber)
				numboxes = V.MaximumSLACBoxSizes;
		} else if (V.UserWantsToSpecifyNumberOfBoxes)
		{
			numboxes = V.MaximumRegularBoxCountBoxes;
		}

		if (numboxes < V.leastpossibleboxes)
		{
			numboxes = V.leastpossibleboxes;
		} else if (numboxes > V.absolutemaximumnumberofboxes)
		{
			numboxes = V.absolutemaximumnumberofboxes;
		}
		// now the upper limits are set in stone
		V.MinBoxSize = 1;
		if (V.MF)
		{
			if (V.MFUserMinBoxSize > 0)
				V.MinBoxSize = V.MFUserMinBoxSize;
		} else if (V.SLAC)
		{
			if (V.LacUserMinBoxSize > 0)
				V.MinBoxSize = V.LacUserMinBoxSize;
		} else if (V.BoxCountUserMinBoxSize > 0)
		{
			V.MinBoxSize = V.BoxCountUserMinBoxSize;
		}
		if (V.MinBoxSize < 1)
			V.MinBoxSize = 1;
		// if this is a vertical or horizontal line roi then adjust
		// the box sizes so that the scan is not aborted
		if (MaxBoxSize <= (V.MinBoxSize))
			MaxBoxSize = V.MinBoxSize + 1;

		int increment = (int) ((float) (MaxBoxSize - V.MinBoxSize) / (float) numboxes);
		if (increment < 1)
			increment = 1;

		int[] BS = new int[numboxes];
		int index = 1;

		if (V.power)
		{
			float exp = V.exponent;
			BS[0] = (int) V.base;
			for (index = 1; (BS[index - 1] < V.limit) && (index < numboxes - 1); index++)
			{
				int f = (int) Math.pow(V.base, exp);
				if (f > BS[index - 1])
					BS[index] = f;
				else
					index--;
				exp = exp + V.exponent;
			}
		}

		else if (V.ScaleTheBoxSizes)
		{
			float Scale = V.BoxSizeScaleDenominator / V.BoxSizeScaleNumerator;
			// the above are switched because we need scale to the ^-1
			float M = (V.ScaledMaxBoxSize == 0) ? V.limit : V.ScaledMaxBoxSize;
			int[] scaledsizes = MakeScaledSeries(M, Scale, V.MinBoxSize);
			index = scaledsizes.length;
			BS = new int[index];
			for (int b = 0; b < index; b++)
				BS[b] = scaledsizes[b];
			// set the smallest size in BS to V.MinBoxSize, and reorder the
			// array

		} else
		{
			BS[0] = V.MinBoxSize;

			for (index = 1; ((BS[index - 1] < V.limit) && (index < numboxes)); index++)
			{
				BS[index] = BS[index - 1] + increment;
			}
			if (index > 1)
				BS[index - 1] = V.limit;
			else
				V.limit = BS[index - 1];
		}
		if (!V.isMF() && !V.SLAC)
		{
			if (index < V.leastpossibleboxes
					&& !V.UserWantsToSpecifyNumberOfBoxes)
			{
				int[] newarray = new int[V.leastpossibleboxes];
				// int first=BS[0]>1?BS[0]:1;
				for (int k = 0; k < V.leastpossibleboxes; k++)
				{
					newarray[k] = k < index ? BS[k] > 1 ? BS[k] : 1
							: newarray[k - 1] + increment;
				}
				BS = new int[V.leastpossibleboxes];

				for (int j = 0; j < V.leastpossibleboxes; j++)
					BS[j] = newarray[j];
				V.limit = BS[BS.length - 1];
				index = V.leastpossibleboxes;

			}
		}
		if (V.UserWantsToSpecifyNumberOfBoxes
				&& index < V.MaximumRegularBoxCountBoxes)
		{
			if (!V.power && !V.ScaleTheBoxSizes)
			{
				int[] Temp = new int[V.MaximumRegularBoxCountBoxes];
				for (int i = 0; i < V.MaximumRegularBoxCountBoxes; i++)
				{
					if (i < index)
						Temp[i] = BS[i];
					else
						Temp[i] = Temp[i - 1] + increment;
				}
				BS = new int[Temp.length];
				for (int i = 0; i < BS.length; i++)
					BS[i] = Temp[i];
				index = Temp.length;// make as many boxes as requested
			}
		}

		// if(issub){
		// if(V.UserSubboxsize);
		// else;
		// }
		F.boxSizeOverMaxWidth = new float[index];
		F.boxSizes = new int[index];
		float L = (float) V.GreaterOfRectangleHeightAndWidth;

		if (V.cumulative)
			V.Subboxsize = (int) ((V.percentsubboxsize / 100.00f) * L);

		if (issub)
			L = (float) V.Subboxsize;

		for (int m = 0; m < index; m++)
		{
			F.boxSizeOverMaxWidth[m] = (float) BS[m] / L;
			F.boxSizes[m] = BS[m];
		}
		// ////////////////////initialize arrays for scans//////////////////
		F.AvgPixAtLocAndEpsilon = new float[V.NumberOfGridPositions][index];
		F.Lacunarity = new float[V.NumberOfGridPositions][index];
		F.ELacunarity = new float[V.NumberOfGridPositions][index];
		//F.LacunarityCount = new float[V.NumberOfGridPositions][index];
		//F.LacEmptiesCount = new float[V.NumberOfGridPositions][index];
		F.AvgPixPlusEmpties = new float[V.NumberOfGridPositions][index];

		F.AveragePixOverTotalPix = new float[V.NumberOfGridPositions][index];

		F.StdDevInPixelsPerBox = new float[V.NumberOfGridPositions][index];
		F.TestArea = new float[V.NumberOfGridPositions][index];
		F.TestAreas = new float[index];
		F.StdDevPlusEmpties = new float[V.NumberOfGridPositions][index];
		//F.MinArray = new int[index][];
		F.SqrdCVforPixelsPerBox = new float[V.NumberOfGridPositions][index];

		F.ESqrdCVforPixelsPerBox = new float[V.NumberOfGridPositions][index];

		F.binnedPDLAtEpsilon = new float[V.NumberOfGridPositions][index];
		F.binnedMeanOfProbDistributionAtEpsilon = new float[V.NumberOfGridPositions][index];
		F.binnedStdDevOfPDistributionAtEpsilon = new float[V.NumberOfGridPositions][index];
		F.binnedCVofProbDensityAtEpsilon = new float[V.NumberOfGridPositions][index];

		//F.ListCounters = FLutil.newArray(index, 0);

		initializeFreqArray(index, F);
		// //debug(35);
		return index;
	}

	private static ImageProcessor expandImage(ImageProcessor ipOld, int wNew,
			int hNew, int xOff, int yOff, int background)
	{/* begin function to add border */
		ImageProcessor ipNew = ipOld.createProcessor(wNew, hNew);
		Color c = Color.white;
		if (background == 0)
			c = Color.black;

		ipNew.setColor(c);

		ipNew.fill();
		ipNew.insert(ipOld, xOff, yOff);
		return ipNew;
	}/* end add border */

	private static ImageProcessor ready(String Title, ImagePlus img,
			boolean issub, FLEmu F, FLVars V)
	{
		// to get ready for the box count,
		// make a new processor that has a border around it

		ImageProcessor thisip = img.getProcessor();
		//F.sizestring = "";
		//F.countstring = "";
		boolean parts = false;
		if (V.isUseParticleAnalyzer() || V.UseRectangularAnalyzer)
			parts = true;
		ImageStatistics stats = img.getStatistics();
		V.TotalAllPixels = stats.histogram[255] + stats.histogram[0];

		if (V.TotalAllPixels != stats.pixelCount)
		{
			System.out.println("Binary image required");
			V.abort = true;

		} else if (!V.abort)
		{// if this analysis is not using the particle analyzer or
			// rectangular analyzer
			if (!parts)
			{// set the foreground to the colour
				// that is least prevalent
				if (stats.histogram[0] > stats.histogram[255])
				{
					V.setForeground(255);
					V.setBackground(0);
				} else
				{
					V.setForeground(0);
					V.setBackground(255);
				}
				if (V.ChooseBackground == FLVars.USEBLACK)
				{
					V.background = FLVars.BLACK;
					V.foreground = FLVars.WHITE;
				}
				if (V.ChooseBackground == FLVars.USEWHITE)
				{
					V.background = FLVars.WHITE;
					V.foreground = FLVars.BLACK;
				}
			}
			V.TotalForegroundPixels = stats.histogram[V.getForeground()];
			V.PixelRatio = V.TotalForegroundPixels / V.TotalAllPixels;

			img.killRoi();/* no roi on original image */
			V.Margins = FindMargins(thisip, F, V);
			if (V.Margins == null)
			{
				V.abort = true;
				return null;
			} else
				V.abort = false;
			int margright = V.Margins[1];
			int margleft = V.Margins[0];
			int margtop = V.Margins[2];
			int margbottom = V.Margins[3];

			if (!V.isAbort())
			{
				if (V.LesserOfRectangleHeightAndWidth < 2)
					MakeNewBoxSizes(2, issub, F, V);
				else
				{
					MakeNewBoxSizes(V.LesserOfRectangleHeightAndWidth, issub,
							F, V);
				}

				float mx = V.maxpercent / 100.00f;

				V.border = (int) Math.max(mx * (float) (margright - margleft),

				mx * (float) (margbottom - margtop));

				if (issub && V.isRandomsquares())
					V.border = V.Subboxsize;
				// if(issub&&!V.isRandomsquare())V.border=
				// //debug(9);
				int wOld, hOld, wNew, hNew;/* variables for borders */
				wOld = thisip.getWidth();/* get the image's width and height */
				hOld = thisip.getHeight();
				wNew = wOld + V.border;/*
										 * set up a new value for the added
										 * border
										 */
				hNew = hOld + V.border;/*
										 * set up a new value for the added
										 * border
										 */
				V.xOffset = (wNew - wOld) / 2; // offset for centering
				V.yOffset = (hNew - hOld) / 2; // offset for centering
				ImageProcessor newIP;
				// make a copy with a new border; based on ImageJ plugin Resize
				// Canvas
				newIP = expandImage(thisip, wNew, hNew, V.xOffset, V.yOffset,
						V.getBackground());
				thisip = newIP;// thisip=//FLResizer.addborder(thisip,V.border,
								// V.OriginalImageTitle,
				// V.getBackground());

				V.Margins = null;
				V.Margins = FindMargins(thisip, F, V);
				if (V.Margins == null)
				{
					V.abort = true;
					return null;

				} else
				{

					V.expandeddrawing = new ImagePlus("ExDrawing", thisip);
				}
			}
		}
		return thisip;
	}

	private static int[] moveGridalloverCountPixels(boolean issub, int boxnum,
			int xstart,
			int ystart,
			// int sz,
			boolean doSLIDINGLACUNARITY, int slidex, int slidey,
			int subsamplenumber, int thislocation, int bot, int rt,
			ImageProcessor imap,

			FLEmu F, FLVars V)

	{
		if (slidex < 1)
			slidex = 1;
		if (slidey < 1)
			slidey = 1;
		int countboxes = 0;
		// boolean done = false;
		int numPix = 0;
		// Roi p = null;
		int sz = F.boxSizes[boxnum];

		if (!issub)
		{// adjust multifractal scans that are not random mass
			if (V.BottomRight && !V.cumulative)
			{
				float w = V.Margins[1] - V.Margins[0];// margright-margleft;
				float h = V.Margins[3] - V.Margins[2];// margbottom-margtop;

				if (thislocation == 1)
				{
					xstart = (int) (V.Margins[1] - (Math.ceil(w / (float) sz) * (float) sz));
					ystart = (int) (V.Margins[3] - (Math.ceil(h / (float) sz) * (float) sz));

				} else if (thislocation == 2)
				{
					xstart = (int) (V.Margins[0]);
					ystart = (int) (V.Margins[3] - (Math.ceil(h / (float) sz) * (float) sz));
				} else if (thislocation == 3)
				{
					xstart = (int) (V.Margins[1] - (Math.ceil(w / (float) sz) * (float) sz));
					ystart = (int) (V.Margins[2]);
				}

			}
		}

		if (V.slipGrid && !doSLIDINGLACUNARITY)
		{
			Random R = new Random();
			xstart = F.RectangleEnclosingForeground.x - R.nextInt(V.border);
			ystart = F.RectangleEnclosingForeground.y - R.nextInt(V.border);
		}

		// IJ.showStatus("Scanning pixels");
		int possiblepix = ((bot - ystart) + sz) * ((rt - xstart) + sz);
		// (Aug 2, 2005)FIXME size: make a new array to hold pixel counts
		int[] PPBr = new int[possiblepix];
		F.empties = 0;
		if (!doSLIDINGLACUNARITY)// &&!V.NewRandomMassMethod)
		{
			slidex = sz;
			slidey = sz;
		}

		// one time, check the total pixels
		// and the total foreground pixels in this area
		if (issub)
		{
			rt = xstart + V.Subboxsize;
			bot = ystart + V.Subboxsize;
			if (boxnum == 0)
			{
				imap.setRoi(xstart, ystart, V.Subboxsize, V.Subboxsize);
				int[] histogram = imap.getHistogram();
				F.ForegroundPixSampled[subsamplenumber] = histogram[V
						.getForeground()];
				F.TotalPixSampled[subsamplenumber] = FLutil.SumInts(histogram,
						256);

			}
		}
		if (slidey < 1)
			slidey = 1;
		if (slidex < 1)
			slidex = 1;

		if (V.isQuick())
		{
			for (int y = ystart; y <= bot; y = y + slidey)
			{
				// count at all the boxes
				for (int x = xstart; x <= rt; x = x + slidex)
				{

					if (V.BLOCKTEXTUREANALYSIS
							&& (((x + sz) > rt) || ((y + sz) > bot)))
						;
					else
					{
						// if(debug) IJ.log("x="+x + " at "+sz +
						// "; right is "+rt);

						{
							if (y == ystart && x == xstart)
							{
								// System.out.println(imap.getWidth()+"/"+imap.getHeight());
								V.StartX = x;
								V.StartY = y;
							} else
							{
								V.EndX = x + sz;
								V.EndY = y + sz;
							}
							imap.setRoi(x, y, sz, sz);

							if (!V.cumulative)
								F.TestArea[thislocation][boxnum] =

								(float) (((x + sz) - xstart) * ((y + sz) - ystart));
							F.TestAreas[boxnum] = (float) (((x + sz) - xstart) * ((y + sz) - ystart));
							int[] histogram = imap.getHistogram();
							numPix = histogram[V.getForeground()];
							// imap.moveTo(x, y); imap.lineTo(x+sz, y+sz);
						}
						if (numPix > 0)
						{
							countboxes++;
							// /count if there were pixels in the box
							PPBr[countboxes - 1] = numPix;// ///
						} else
						{
							F.empties++;
						}

					}
				}
			}
		} else
			for (int y = ystart; y <= bot; y = y + slidey)
			{// count at all the boxes
				for (int x = xstart; x <= rt; x = x + slidex)
				{

					int tempw = sz, temph = sz;
					if ((x + sz) > rt)
						tempw = (rt - x);
					if ((y + sz) > bot)
						temph = (bot - y);
					if (V.BLOCKTEXTUREANALYSIS
							&& (((x + sz) > rt) || ((y + sz) > bot)))
						;
					else
					{
						if (y == ystart && x == xstart)
						{
							V.StartX = x;
							V.StartY = y;
						} else
						{
							V.EndX = x + tempw;
							V.EndY = y + temph;
						}

						imap.setRoi(x, y, tempw, temph);
						int[] histogram = imap.getHistogram();

						if (!V.cumulative)
							F.TestArea[thislocation][boxnum] =

							(((x + tempw) - xstart) * ((y + temph) - ystart));
						F.TestAreas[boxnum] = (float) (((x + sz) - xstart) * ((y + sz) - ystart));
						numPix = histogram[V.getForeground()];

						if (numPix > 0)
						{
							countboxes++;
							/* count if there were pixels in the box */
							PPBr[countboxes - 1] = numPix;// ///
						} else
						{
							F.empties++;
						}
					}

				}
			}

		// IJ.showStatus("Finished grid "+boxnum);

		if ((!V.isUseParticleAnalyzer()) && (!V.UseRectangularAnalyzer))
		{
			V.setActualx(xstart);/* set these for printing the box locations */
			V.setActualy(ystart);/* for printing the box locations */
		}
		V.actualbot = bot;
		V.actualrt = rt;
		int holder = countboxes;
		int[] PPBFinal = new int[holder];
		for (int a = 0; a < holder; a++)
			PPBFinal[a] = PPBr[a];
		return PPBFinal;// this is an array containing the number of
		// pixels that were in each box at this size

	}

	private static int[] CountPix(int ThisSubsample, int THISLOCATION,
			boolean asub, int boxnumber,
			// int sze,
			ImageProcessor ip, int x, int y, boolean doslide, int slidex,
			int slidey,

			FLEmu F, FLVars V)
	{
		int rit = V.Margins[1];// ip.getWidth();//(int)margright;
		// for main analysis, set the right edge of the total pixelated area
		int botm = V.Margins[3];// ip.getHeight();//(int)margbottom;//set bottom
								// for main
		if (asub && (V.BottomRight == false))
		{
			rit = x + V.limit;
			botm = y + V.limit;
		}
		// for subareas only, reset the boundaries of the
		// area to check using the values for the subarea size
		// call function that counts the pixels in each box and the
		// number of boxes having pixels

		int[] ppb = moveGridalloverCountPixels(asub, boxnumber, x, y,
				// sze,
				doslide, slidex, slidey, ThisSubsample, THISLOCATION, botm,
				rit, ip, F, V);

		return ppb;
	}

	private static int[] AddZerosToArray(int[] array, int length, int newlength)
	{
		int[] newarray = new int[newlength];

		for (int i = 0; i < newlength; i++)
		{
			if (i < length)
				newarray[i] = array[i];
			else
				newarray[i] = 0;
		}
		return newarray;
	}

	private static float[][] SummedPAtEpsilon(float[] P, float[] QS, float[] M,
			FLVars V)
	{
		float[] SummedProbsToQ = FLutil.newArray(V.Qs.length, 0.0f);
		float[] DS = FLutil.newArray(V.Qs.length, 0.0f);
		float[] Mass = FLutil.newArray(V.Qs.length, 0.0f);
		float[] A = FLutil.newArray(V.Qs.length, 0.0f);
		float[] F = FLutil.newArray(V.Qs.length, 0.0f);
		float SummedMass = FLutil.SumArray(M, M.length);
		// System.out.println("no ploblem");
		// if (M.length!=P.length)System.out.println("ploblem");
		for (int q = 0; q < QS.length; q++)
		{
			// For each box

			// Sum all the probabilities in the array
			// to the exponent Q,// unless Q=1,
			for (int i = 0; i < P.length; i++)
			{
				if (QS[q] == 1f)
					DS[q] = DS[q] + (float) Math.log(P[i]) * P[i];
				else
					DS[q] = DS[q] + (float) Math.pow(P[i], QS[q]);
				SummedProbsToQ[q] = SummedProbsToQ[q]
						+ (float) Math.pow(P[i], QS[q]);

			}
			for (int i = 0; i < M.length; i++)
			{
				Mass[q] = Mass[q]
						+ (float) Math.pow((M[i] / SummedMass), QS[q] - 1.0f);
			}

			for (int i = 0; i < P.length; i++)
			{
				// Sum the alphas and f(alphas)
				// using the method of Chhabra and Jensen:
				// mu(i) = [p(i)^Q]/SUM[P(i to j)^Q]
				// alpha = SUM[mu*ln{P(i)}]/ln{epsilon}
				// f(alpha) = SUM[mu*ln{mu}]/ln{epsilon}
				float mu = (float) Math.pow(P[i], QS[q]) / SummedProbsToQ[q];

				A[q] = A[q] + (mu * (float) Math.log(P[i]));
				F[q] = F[q] + (mu * (float) Math.log(mu));

			}
			// store the mean also
			Mass[q] = Mass[q] / (float) P.length;
		}

		float[][] ST = new float[4][V.Qs.length];
		ST[0] = DS;// this is now the Generalized Dimensions array
		ST[1] = Mass;// this is the taus by an alternative mean method array
		ST[2] = A;// this is the alpha values array
		ST[3] = F;// this is the f at alpha values,
		// the multifractal spectrum array

		return ST;

	}

	private static String PrintAndGraph(float[][] finalDQS, float[][] finalxDQS,
			String[] qlines, int[][] AllCounts, float[][] finaltaus,
			float[][] finalF, float[][] finalA, int thissubsample,
			String thistype, FLEmu FE, FLVars V)
	{
		if (!V.WriteQslopeandCV && V.isMF())
			MakeQHeading(FE, V);
		// make headings for generalized dimensions

		// System.out.println("PrintAndGraph");

		String Qslopes = "";
		// find the optimized generalized dimension spectrum
		int bestLOC = 0;
		float bestCV = 0;
		if ((V.GraphAnyQs() || V.isMF()) && V.SelectBestCV)
		{
			bestCV = FLstatistics.CoefVar(finalDQS[0], V.Qs.length);
			for (int i = 0; i < V.NumberOfGridPositions; i++)
			{
				float test = FLstatistics.CoefVar(finalDQS[i], V.Qs.length);
				if (test > bestCV)
				{
					bestCV = test;
					bestLOC = i;
				}
			}
		}
		if (V.isMF() && V.SelectBestCV)
		{
			// System.out.println("SOMETHING");

			FracStats F = new FracStats();
			float[] stats = F.PlainLinearRegression(finalDQS[bestLOC], V.Qs,
					V.Qs.length);
			Qslopes = FLEmu.tab + bestLOC + FLEmu.tab + FLutil.fnum(bestCV, 6)
					+ FLEmu.tab + FLutil.fnum(stats[0]) + FLEmu.tab
					+ FLutil.fnum(stats[1]);

			// if(V.WriteQslopeandCV)
			// {
			// System.out.println("WriteQslopeandCV");
			// }
			// else
			{
				// System.out.println("Not WriteQslopeandCV");

				V.data = V.data + // IJ.write
						(Qslopes);
				V.data = V.data + // IJ.write
						(qlines[bestLOC]);
			}
		} else
		// print generalized dimensions and frequencies and masses

		if (V.isMF() || V.printFrequencies)
		{
			System.out.println("printFrequencies");
			for (int i = 0; i < qlines.length; i++)
				V.data = V.data + // IJ.write
						(qlines[i]);
		}

		if (!V.SkipRawData)
		{
			System.out.println("printRawData");
			// V.RawDataString=
			// V.RawDataString+printRawData(AllCounts, FE, V); //print raw data
		}

		return Qslopes;
	}

	private static int[][] doBoxCounts(boolean wassub, ImageProcessor ipc,
			int x, int y, boolean doSLIDINGLACUNARITY, int slidex, int slidey,
			int LOC, int Thissubn,
			int CountOfBoxesHavingPixelsAtLocationAndEpsilon[][], FLEmu F,
			FLVars V)
	{
		System.out.println("doBoxCounts(" + x + ", " + y + ", " + slidex + ", " + slidey + ")");
		System.out.println("boxNumber = " + F.boxSizes.length);
		for(int i = 0; i < F.boxSizes.length; ++i)
			System.out.println("boxSize[" + i + "] = " + F.boxSizes[i]);

		int[][] p = new int[F.boxSizes.length][];
		for (int boxnumber = 0; boxnumber < F.boxSizes.length; boxnumber++)
		{
			// Goes through for each box size, and counts
			// pixels using that box size
			// PpB is made as an array containing one value for each
			// box's number of pixels--that is, how many pixels were
			// in each box that had boxes at this particular size
			// The count at this box size is therefore the length of PpB.

			int[] PixelsPerBox

			= CountPix(

			Thissubn, LOC, wassub, boxnumber,
			// boxSizes[boxnumber],
					ipc, x, y, doSLIDINGLACUNARITY, slidex, slidey, F, V);

			int CopyPPB[] = new int[PixelsPerBox.length];
			for (int i = 0; i < PixelsPerBox.length; i++)
				CopyPPB[i] = PixelsPerBox[i];
			int boxes = CopyPPB.length;
			{
				FLstatistics stats = new FLstatistics(CopyPPB, boxes);
				F.AvgPixAtLocAndEpsilon[LOC][boxnumber] = stats.mean;
				F.AveragePixOverTotalPix[LOC][boxnumber] = F.AvgPixAtLocAndEpsilon[LOC][boxnumber]
						/ V.TotalForegroundPixels;
				F.StdDevInPixelsPerBox[LOC][boxnumber] = stats.stddev;
				F.SqrdCVforPixelsPerBox[LOC][boxnumber] = (stats.mean == 0) ? 0f
						: (stats.stddev * stats.stddev)
								/ (stats.mean * stats.mean);
				int[] totals = AddZerosToArray(CopyPPB, boxes, boxes
						+ F.empties);
				stats = new FLstatistics(totals, boxes + F.empties);
				F.AvgPixPlusEmpties[LOC][boxnumber] = stats.mean;
				F.StdDevPlusEmpties[LOC][boxnumber] = stats.stddev;
				F.ESqrdCVforPixelsPerBox[LOC][boxnumber] = (stats.mean == 0) ? 0f
						: (stats.stddev * stats.stddev)
								/ (stats.mean * stats.mean);
				if (wassub)
					F.Lacunarity[LOC][boxnumber] = 0f;
				else
					F.Lacunarity[LOC][boxnumber] = FLstatistics
							.LacunarityCoefficientofVariation(CopyPPB, boxes);
				F.ELacunarity[LOC][boxnumber] = FLstatistics
						.LacunarityCoefficientofVariation(totals, boxes
								+ F.empties);
				CountOfBoxesHavingPixelsAtLocationAndEpsilon[LOC][boxnumber] = boxes;
				F.NumberOfEmptyBoxesAtLocationAndEpsilon[LOC][boxnumber] = F.empties;
				float[] Densities = null;
				if (V.printFrequencies || V.GraphAnyQs() || V.isMF())
				{
					Densities = FLutil.MeanProbArray(CopyPPB, boxes);
				}
				if (V.GraphAnyQs() || V.isMF())
				{
					// at each epsilon, sum the probabilities to the Q
					// for calculating the multifractal spectrum
					// if the slope removed cover is being sought, skip any
					// epsilon that had the same count as the previous
					float[][] A = SummedPAtEpsilon(Densities, V.Qs,
							FLutil.IntToFloatArray(CopyPPB), V);
					F.FullSummedPtoQAtEpsilons[boxnumber] = A[0];
					F.FullMeanTau[boxnumber] = A[1];
					F.FullAlphaArray[boxnumber] = A[2];
					F.FullFArray[boxnumber] = A[3];
				}

				// bins at 0; probabilities at 1
				// /binnedmprob holds an array of the probabilities
				// sorted into categories instead of for each individual value
				// so it has two arrays, one for the value
				// and one for the frequency of that value

				float[][] binnedmprob = { { 0f }, { 1f } };

				binnedmprob = (boxes == 0) ? binnedmprob : FLstatistics
						.binnedProbabilityDistribution(CopyPPB, boxes,
								V.MaxFrequencies);
				if (binnedmprob == null)
					V.data = V.data + // IJ.write
							("null returned");
				/*
				 * if(V.printFrequencies) { String [] twostrings=null;
				 * twostrings = PrepareaStringOfFrequencies(binnedmprob,
				 * boxnumber,F, V); F.Frequencies[LOC][boxnumber]=
				 * twostrings[0]; F.BigFrequencies[LOC][boxnumber] =
				 * FLutil.ArrayToString(Densities); F.Masses[LOC][boxnumber]=
				 * twostrings[1]; }
				 */

				// Find the lacunarity statistics using the unsorted
				// distribution and a binned distribution

				float[] binnedPstuff = FLutil.BinnedProbabilityStats(
						binnedmprob, LOC);
				if (binnedPstuff == null)
					binnedPstuff = FLutil.newArray(4, 0.0f);

				F.binnedMeanOfProbDistributionAtEpsilon[LOC][boxnumber] = binnedPstuff[0];

				F.binnedStdDevOfPDistributionAtEpsilon[LOC][boxnumber] = binnedPstuff[1];

				F.binnedCVofProbDensityAtEpsilon[LOC][boxnumber] = binnedPstuff[2];

				F.binnedPDLAtEpsilon[LOC][boxnumber] = binnedPstuff[3];

			}
			p[boxnumber] = new int[PixelsPerBox.length];
			for (int i = 0; i < PixelsPerBox.length; i++)
				p[boxnumber][i] = PixelsPerBox[i];
		}// END loop for all epsilons

		// now all the logs of sums of p to the q are arranged in an
		// array with one array for each epsilon
		// //debug("finished box counts");
		// if(V.WriteMasses)recordMasses(p, LOC, F, V);

		// find the minimum or slope corrected
		// arrays for multifractal analyses
		if ((V.isMF()) && (V.dataoption == 1))
		{
			System.out.println("SLOPE 1");
			doSlopeMF(p, LOC, Thissubn, true, F, V);
		}

		if (!V.cumulative)
		{
			if (V.isMF() && ((V.dataoption == 2) || (V.dataoption == 3)))
			{
				System.out.println("SLOPE 2");
				// MinimumArray(p, LOC, F, V);
			}
		}

		return CountOfBoxesHavingPixelsAtLocationAndEpsilon;
	}// end doboxcounts

	

	private static float[][] FinishQslopesForAllEpsilonAtLocation(
			float[][] SUMMEDPtoQatEPSILONS, float[][] MEANTAU, float[][] ALPHA,
			float[][] FatALPHA, int LOC, FLEmu F, FLVars V)
	{

		int N = F.boxSizes.length;
		float[][] Sumptq = FLutil.new2dArray(V.Qs.length, F.boxSizes.length,
				0.0f);
		float[][] SummedTau = FLutil.new2dArray(V.Qs.length, F.boxSizes.length,
				0.0f);
		float[][] SumAlpha = FLutil.new2dArray(V.Qs.length, F.boxSizes.length,
				0.0f);
		float[][] SumF = FLutil
				.new2dArray(V.Qs.length, F.boxSizes.length, 0.0f);

		// float [] InverseEpsilons = FLutil.FloatArrayToInverseFloatArray(
		// F.boxSizeOverMaxWidth, F.boxSizes.length);

		float[] LogInverseEpsilons = FLutil.FloatArrayToLogInverseFloatArray(
				F.boxSizeOverMaxWidth, F.boxSizes.length);
		float[] BoxSizeAreaOverPixelArea = new float[F.boxSizes.length];
		for (int i = 0; i < F.boxSizes.length; i++)
		{
			BoxSizeAreaOverPixelArea[i] = (float) Math
					.sqrt((((float) F.boxSizes[i] * (float) F.boxSizes[i]) / F.TestArea[LOC][i]));
		}

		for (int e = 0; e < N; e++)
		{
			for (int q = 0; q < V.Qs.length; q++)
			{
				Sumptq[q][e] = SUMMEDPtoQatEPSILONS[e][q];
				SummedTau[q][e] = MEANTAU[e][q];
				SumAlpha[q][e] = ALPHA[e][q];
				SumF[q][e] = FatALPHA[e][q];
			}

		}

		float[] thisxDqs = FLutil.newArray(V.Qs.length, 0.0f);
		float[] thisDqs = FLutil.newArray(V.Qs.length, 0.0f);
		float[] thisTaus = FLutil.newArray(V.Qs.length, 0.0f);
		float[] thisAlphas = FLutil.newArray(V.Qs.length, 0.0f);
		float[] thisFs = FLutil.newArray(V.Qs.length, 0.0f);

		FracStats QStats;
		for (int q = 0; q < V.Qs.length; q++)
		{
			if (V.Qs[q] == 1)
			{
				QStats = new FracStats(Sumptq[q], LogInverseEpsilons, N);

				thisDqs[q] = -QStats.Pslope;

				thisxDqs[q] = 0.0f;
				thisTaus[q] = 0.0f;

				QStats = new FracStats(SumAlpha[q], LogInverseEpsilons, N);
				thisAlphas[q] = -QStats.Pslope;

				QStats = new FracStats(SumF[q], LogInverseEpsilons, N);
				thisFs[q] = -QStats.Pslope;

			} else
			{
				QStats = new FracStats(SummedTau[q], BoxSizeAreaOverPixelArea,
						N);
				thisTaus[q] = QStats.df;//

				QStats = new FracStats(SumAlpha[q], LogInverseEpsilons, N);
				thisAlphas[q] = -QStats.Pslope;

				QStats = new FracStats(SumF[q], LogInverseEpsilons, N);
				thisFs[q] = -QStats.Pslope;

				thisxDqs[q] = thisFs[q] - (V.Qs[q] * thisAlphas[q]);
				thisDqs[q] = thisxDqs[q] / (float) (1.0f - V.Qs[q]);

			}

		}

		float[][] D = { thisxDqs, thisDqs, thisTaus, thisAlphas, thisFs };
		return D;
	}

	private static String FormatTitle(String OriginalImageTitle,
			int GridLocationNumber, int thisiteration,
			int xForBoundingRectangle, int yForBoundingRectangle,
			String ROITYPE, int widthOfBoundingRectangle,
			int heightOfBoundingRectangle)
	{
		return new String(OriginalImageTitle + "(" + xForBoundingRectangle
				+ "," + yForBoundingRectangle + ":" + widthOfBoundingRectangle
				+ "x" + heightOfBoundingRectangle + ")" + ROITYPE + "/L="
				+ GridLocationNumber);
	}

	private static String PrepareStringsFromThisOrigin(float[][] pDqs,
			float[][] pxDqs, float[][] pTAUS, float[][] pALFAS, float[][] pFS,
			String ssddd, int x, int y, int thisiteration, FracStats thisl,
			StoreAvgs avs, int LOC, int OriginalWidth, int OriginalHeight,
			FLEmu F, FLVars V)
	{
		// boolean parts=false;
		// String data = "";
		// if(V.isUseParticleAnalyzer()||V.UseRectangularAnalyzer)parts=true;

		String BigString = "";
		if (V.isMF() || V.printFrequencies)
		{

			// String tabs = FLEmu.tab+FLEmu.tab+FLEmu.tab;

			BigString = FLEmu.newline
					+ "PROBABILITY DATA FOR "
					+ FormatTitle(V.OriginalImageTitle, LOC, 0, x, y, "Roi",
							OriginalWidth, OriginalHeight) + FLEmu.tab + ssddd
					+ FLEmu.newline;
			if (V.isMF())
			{
				FracStats FS = new FracStats(pDqs[LOC], V.Qs, V.Qs.length);
				FracStats SL = new FracStats(F.AvgPixAtLocAndEpsilon[LOC],
						F.boxSizes, F.boxSizes.length);
				String gg = "Mass Dimension=" + FLEmu.tab + -SL.df
						+ FLEmu.newline;
				BigString = BigString + gg;
				BigString = BigString
						+ "GENERALIZED DIMENSIONS over all Q"
						+ FLEmu.tab
						+ "CV for DQ"
						+ FLEmu.tab
						+ FLutil.fnum(FLstatistics.CoefVar(pDqs[LOC],
								V.Qs.length)) + FLEmu.tab + "slope for D(Q)vsQ"
						+ FLEmu.tab + FLutil.fnum(FS.Pslope) + FLEmu.newline
						+ "Q" + FLEmu.tab + "Dqs at Q" + FLEmu.tab
						+ "Tau (slope)" + FLEmu.tab + "Mean Tau" + FLEmu.tab
						+ "alpha" + FLEmu.tab + "f" + FLEmu.newline;
				String qstring = "";
				for (int q = 0; q < V.Qs.length; q++)
					qstring = qstring + V.Qs[q] + FLEmu.tab
							+ FLutil.fnum(pDqs[LOC][q]) + FLEmu.tab
							+ FLutil.fnum(pxDqs[LOC][q]) + FLEmu.tab
							+ FLutil.fnum(pTAUS[LOC][q]) + FLEmu.tab
							+ FLutil.fnum(pALFAS[LOC][q]) + FLEmu.tab
							+ FLutil.fnum(pFS[LOC][q]) + FLEmu.newline;
				BigString = BigString + qstring;

			}

		}

		return BigString;
	}

	// DATA WRITING
	private static void MakeQHeading(FLEmu F, FLVars V)
	{// print columns of q and the corresponding Dq
		String d = "";
		d = (// IJ.write(
		d + FLEmu.newline + "GENERALIZED DIMENSION AT Q and MULTIFRACTAL SPECTRUM");
		d = (// IJ.write(
		d + "Tau=lim[ln(I(Q,E))/ln(1/E)]" + FLEmu.tab + "D(Q)=Tau/(1-Q)"
				+ FLEmu.tab + "I(Q,E)=SUM[P(i)^Q], where SUM[P(i)]=1"
				+ FLEmu.tab + FLEmu.newline + "P(i)=density"
				+ "for all boxes (i) at this E (epsilon)" + FLEmu.tab);
		d = (// IJ.write(
		d + "mu=P(i)^Q/SUM[P(i)^Q" + FLEmu.tab + "alpha = SUM[mu(i)*P(i)]/lnE"
				+ FLEmu.tab + "f(alpha)= SUM[mu*ln(mu)]/lnE");

		V.data = V.data + d;
	}

	private static float[][] FinishQslopes(float[][] SUMMEDPtoQatEPSILONS,
			float[][] MEANTAU, float[][] ALPHA, float[][] FatALPHA, int LOC,

			float[] E, int N, float[] QArray,

			FLVars V)
	{

		float[][] Sumptq = FLutil.new2dArray(QArray.length, N, 0.0f);
		float[][] SummedTau = FLutil.new2dArray(QArray.length, N, 0.0f);
		float[][] SumAlpha = FLutil.new2dArray(QArray.length, N, 0.0f);
		float[][] SumF = FLutil.new2dArray(QArray.length, N, 0.0f);

		// float [] InverseEpsilons = FLutil.FloatArrayToInverseFloatArray(
		// E, N);

		float[] LogInverseEpsilons = FLutil.FloatArrayToLogInverseFloatArray(E,
				N);

		for (int e = 0; e < N; e++)
		{
			for (int q = 0; q < QArray.length; q++)
			{
				Sumptq[q][e] = SUMMEDPtoQatEPSILONS[e][q];
				SummedTau[q][e] = MEANTAU[e][q];
				SumAlpha[q][e] = ALPHA[e][q];
				SumF[q][e] = FatALPHA[e][q];
			}

		}

		float[] thisxDqs = FLutil.newArray(QArray.length, 0.0f);
		float[] thisDqs = FLutil.newArray(QArray.length, 0.0f);
		float[] thisTaus = FLutil.newArray(QArray.length, 0.0f);
		float[] thisAlphas = FLutil.newArray(QArray.length, 0.0f);
		float[] thisFs = FLutil.newArray(QArray.length, 0.0f);

		FracStats QStats;
		for (int q = 0; q < QArray.length; q++)
		{
			if (QArray[q] == 1)
			{
				QStats = new FracStats(Sumptq[q], LogInverseEpsilons, N);

				thisDqs[q] = -QStats.Pslope;

				thisxDqs[q] = 0.0f;
				thisTaus[q] = 0.0f;

				QStats = new FracStats(SumAlpha[q], LogInverseEpsilons, N);
				thisAlphas[q] = -QStats.Pslope;

				QStats = new FracStats(SumF[q], LogInverseEpsilons, N);
				thisFs[q] = -QStats.Pslope;

			} else
			{
				QStats = new FracStats(SummedTau[q], E, N);
				thisTaus[q] = QStats.df;//

				QStats = new FracStats(SumAlpha[q], LogInverseEpsilons, N);
				thisAlphas[q] = -QStats.Pslope;

				QStats = new FracStats(SumF[q], LogInverseEpsilons, N);
				thisFs[q] = -QStats.Pslope;

				thisxDqs[q] = thisFs[q] - (V.Qs[q] * thisAlphas[q]);
				thisDqs[q] = thisxDqs[q] / (float) (1.0f - V.Qs[q]);

				// if(V.NewRandomMassMethod)
				// {
				// //Dq=tau/(q-1);
				// thisDqs[q]=thisTaus[q]/((float)q-1.0f);
				// }

			}

		}

		float[][] D = { thisxDqs, thisDqs, thisTaus, thisAlphas, thisFs };
		return D;
	}

	// DATA WRITING
	private static void doSlopeMF(int[][] masses, int LOC, int sample,
			boolean doSlope, FLEmu F, FLVars V)
	{

		// calculate the multifractal data after converting
		// the passed arrays to slope removed

		int[][] M = null;
		if (doSlope)
			M = FLutil.RemoveHorizontalSlopesAndMasses(masses, F.boxSizes,
					F.boxSizes.length);
		int[] newBoxes = F.boxSizes;
		if (doSlope)
			newBoxes = M[0];

		int[][] newmasses = masses;
		if (doSlope)
			newmasses = new int[newBoxes.length][];
		if (doSlope)
		{
			for (int g = 1; g < newBoxes.length; g++)
				newmasses[g] = M[g];
		}

		float[][] slopeDqs = FLutil.new2dArray(newBoxes.length, V.Qs.length,
				0.0f);

		float[][] slopeMeanTau =

		FLutil.new2dArray(newBoxes.length, V.Qs.length, 0.0f);

		float[][] slopeAlphas = FLutil.new2dArray(newBoxes.length, V.Qs.length,
				0.0f);

		float[][] slopeFs = FLutil.new2dArray(newBoxes.length, V.Qs.length,
				0.0f);

		// now get the multifractal calculations done
		// once for each epsilon in the new list
		for (int i = 0; i < newBoxes.length; i++)
		{
			float[] Densities = FLutil.MeanProbArray(masses[i],
					masses[i].length);
			// at each epsilon, sum the probabilities to the Q
			// for calculating the multifractal spectrum
			// if the slope removed cover is being sought, skip any
			// epsilon that had the same count as the previous
			float[][] A = SummedPAtEpsilon(Densities, V.Qs,
					FLutil.IntToFloatArray(masses[i]), V);

			slopeDqs[i] = A[0];
			slopeMeanTau[i] = A[1];
			slopeAlphas[i] = A[2];
			slopeFs[i] = A[3];

		}

		// CVsqrd[e]=varianceP[e]/(MP[e]*MP[e]);
		// }
		float[] E = new float[newBoxes.length];

		for (int i = 0; i < newBoxes.length; i++)
			E[i] = ((float) newBoxes[i])
					/ (float) V.GreaterOfRectangleHeightAndWidth;
		float[][] GD = FinishQslopes(slopeDqs, slopeMeanTau, slopeAlphas,
				slopeFs, LOC, E, newBoxes.length, V.Qs, V);

		String type = doSlope ? "slope corrected" : "minimum cover";
		if (V.dataoption == 3)
			type = "Slope Corrected Minimum Cover";
		// print columns of q and the corresponding Dq
		MakeQHeading(F, V);

		String BigString = "";
		if (V.cumulative)
			BigString = BigString + "Data for Sample " + sample + FLEmu.newline;
		BigString = BigString + "GENERALIZED DIMENSIONS using " + type
				+ " data" + FLEmu.newline + "Q" + FLEmu.tab + "Dqs at Q"
				+ FLEmu.tab + "Tau (slope)" + FLEmu.tab + "Mean Tau"
				+ FLEmu.tab + "alpha" + FLEmu.tab + "f" + FLEmu.newline;
		String qstring = "";
		for (int q = 0; q < V.Qs.length; q++)
			qstring = qstring + V.Qs[q] + FLEmu.tab + FLutil.fnum(GD[1][q])
					+ FLEmu.tab + FLutil.fnum(GD[0][q]) + FLEmu.tab
					+ FLutil.fnum(GD[2][q]) + FLEmu.tab + FLutil.fnum(GD[3][q])
					+ FLEmu.tab + FLutil.fnum(GD[4][q]) + FLEmu.newline;

		V.data = V.data + // IJ.write
				(BigString + qstring);

	}

	private static float writeFinal(String[] qlines, float[][] finalDQS,
			float[][] finalxDQS, float[][] finaltaus, float[][] finalF,
			float[][] finalA, FracStats avgstat, FracStats min,
			ImageProcessor ipk, int XForScannedArea, int YForScannedArea,
			int WidthOfScannedArea, int HeightOfScannedArea, int thisslice,
			int lastslice, StoreAvgs averages, boolean circ, boolean issub,
			String suborglobal, int thissubsample, int xx, int yy,
			int[][] AllCounts, boolean islastsub, int[] roimargins,
			boolean isRectangleSubScan, int ThisRectangleSubScan, FLEmu F,
			FLVars V)
	{
		// only called after all locations are done

		// //////////////////////////////////////////////////////////////////
		// ///// MAKE A BUNCH OF CLASSES FOR CALCULATING /////////////////
		// //// MEANS, STD DEV, CV, ETC., ON DATA FROM ALL LOCATIONS ////////
		// /////////// FROM THE PASSED ARRAYS /////////////////////
		// //////////////////////////////////////////////////////////////////

		FLstatistics Prefactors = new FLstatistics(
				averages.InverseOfPrefactors, V.NumberOfGridPositions);

		// float jjj = 0;
		float avPrefactorLac = 0;
		for (int m = 0; m < V.NumberOfGridPositions; m++)
		{
			avPrefactorLac = avPrefactorLac
					+ ((float) Math
							.pow(((averages.InverseOfPrefactors[m] / Prefactors.mean) - 1.0f),
									2));
		}

		avPrefactorLac = avPrefactorLac / (float) V.NumberOfGridPositions;

		// ///////////////////////////////////////////////////////////////////
		// ///// WRITE STRINGS DESCRIBING THE IMAGE AND /////////////////
		// //// ITS SCANS AND LISTING THE SUMMARIZED DATA FROM ABOVE ///////
		// ///////////////////////////////////////////////////////////////////
		String thistype = V.OriginalImageTitle + thisslice + "("
				+ XForScannedArea + "," + YForScannedArea + ":"
				+ WidthOfScannedArea + "x" + HeightOfScannedArea + ")";

		String ScanDimensions = (V.StartX - V.xOffset) + ","
				+ (V.StartY - V.yOffset) + "-" + (V.EndX - V.xOffset) + ","
				+ (V.EndY - V.yOffset);
		if (V.NumberOfGridPositions == 1 || issub)
			thistype = thistype + ScanDimensions;

		if (!V.SkipRawData)
		{
			String s = V.BoxCountHeadings;
			String D = V.OriginalImageTitle + thisslice + "(All Locations:"
					+ WidthOfScannedArea + "x" + HeightOfScannedArea + ") ";
			V.RawDataString = V.RawDataString + // V.data=V.data+// IJ.write
					(FLEmu.newline + "FINAL SUMMARY with raw data" + FLEmu.tab
							+ D + FLEmu.tab + FLEmu.newline + s);

		}

		PrintAndGraph(finalDQS, finalxDQS, qlines, AllCounts, finaltaus,
				finalF, finalA, thissubsample, thistype, F, V);

		return avgstat.df;

	}// end write final

	private static void finishdata(String[] qlines, float[][] passedDQS,
			float[][] passedxDQS, float[][] passedtaus, float[][] passedF,
			float[][] passedA,

			int thissubsample, int x, int y, ImageProcessor ipk, int XForRoi,
			int YForRoi, int WidthOfRoi, int HeightOfRoi, int thisslice,
			int lastslice, StoreAvgs avs, boolean circ, boolean issub,

			String suborglobal, int[][] AllCounts, boolean islastsub,
			int[] roimargins, boolean isRectangleSubScan,
			int ThisRectangleSubScan, FLEmu F, FLVars V)
	{
		int[] mincover = FLutil.findMinCover(AllCounts,
				V.NumberOfGridPositions, F.boxSizes.length);
		/* returns an array of most efficient covering using all locations */
		float[] avgcover = FLutil.findAvgCover(AllCounts,
				V.NumberOfGridPositions, F.boxSizes.length);
		/* make an array averaged from all locations */
		FracStats mins = new FracStats(mincover, F.boxSizeOverMaxWidth,
				F.boxSizes.length);
		FracStats avgs = new FracStats(avgcover, F.boxSizeOverMaxWidth,
				F.boxSizes.length);
		/* make data compressor for this set of locations */

		writeFinal(qlines, passedDQS, passedxDQS, passedtaus, passedF, passedA,
				avgs, mins, ipk, XForRoi, YForRoi, WidthOfRoi, HeightOfRoi,
				thisslice, lastslice, avs, circ, issub, suborglobal,
				thissubsample, x, y, AllCounts, islastsub, roimargins,
				isRectangleSubScan, ThisRectangleSubScan, F, V);

	}

	private static void ScanImageAtEachLocation(int thissubsample, boolean circ,
			boolean issub, ImageProcessor ipk, int RoiX, int RoiY,
			int RoiWidth, int RoiHeight, int thisslice, int lastslice, int x,
			int y, boolean rSLIDINGLACUNARITY, int slidex, int slidey,
			boolean islastsub, boolean isRectangleSubScan,
			int ThisRectangleSubScan,

			FLEmu F, FLVars V)
	{
		// //////////////////////////////////////////////////////
		// /////// initialize variables and arrays ///////////
		// //////////////////////////////////////////////////////
		StoreAvgs AvgsForAllLocations = new StoreAvgs(V.NumberOfGridPositions);
		// float dddf=0;
		String suborglobal = "global";
		if (issub)
			suborglobal = "local";
		// //debug(620);
		// Initialize AllCounts global array
		int[][] AllCounts = FLutil.new2dArray(V.NumberOfGridPositions,
				F.boxSizes.length, 0);
		//F.Frequencies = new String[V.NumberOfGridPositions][F.boxSizes.length];
		//F.BigFrequencies = new String[V.NumberOfGridPositions][F.boxSizes.length];
		//F.Masses = new String[V.NumberOfGridPositions][F.boxSizes.length];
		float[][] Dqs = new float[V.NumberOfGridPositions][V.Qs.length];
		float[][] xDqs = new float[V.NumberOfGridPositions][V.Qs.length];
		float[][] Taus = new float[V.NumberOfGridPositions][V.Qs.length];
		float[][] Fs = new float[V.NumberOfGridPositions][V.Qs.length];
		float[][] Alphas = new float[V.NumberOfGridPositions][V.Qs.length];

		F.NumberOfEmptyBoxesAtLocationAndEpsilon = new int[V.NumberOfGridPositions][F.boxSizes.length];
		// //debug(3142);
		// epsilon=new float[V.NumberOfGridPositions][boxSizes.length];
		String[] Qlines = new String[V.NumberOfGridPositions];
		// ////////////////////////////////////////////////////////
		// ////////////////////////////////////////////////////////

		int[][] XY = FLutil.MakeSetOfRandomVariables((x > 2) ? x : 3,
				(y > 2) ? y : 3, V.NumberOfGridPositions);
		// make random origins

		System.out.println("ANALIZA : " + RoiX + ", " + RoiY + "  " + RoiWidth + " x " + RoiHeight);
		System.out.println("NumberOfGridPositions = " + V.NumberOfGridPositions);
		
		// //////////////////////////////////////////////////////
		// ///////// Do once for each location ///////////////
		// //////////////////////////////////////////////////////
		for (int locations = 0; locations < V.NumberOfGridPositions; locations++)
		{
			// IJ.showStatus(
			// V.strOldTitle+" "+locations + "/"+NumberOfGridPositions);
			if ((issub) || (locations == 0))
				;

			else
			{// don't use randoms if this is a subarea or
				// this is 1st location
				x = XY[locations][0];
				y = XY[locations][1];
			}
			// System.gc();
			if (issub)
				locations = (V.NumberOfGridPositions - 1);
			// //////////////////////////////////////////////////////
			// ///////// Initialize arrays for each scan //////////
			// //////////////////////////////////////////////////////
			if (V.cumulative && V.isRandomsquares() && V.dosubs)
				locations = V.NumberOfGridPositions;
			else
			{

				AvgsForAllLocations.boxoriginXs[locations] = x;
				// store origins passed
				// which may have varied from those used over all runs
				AvgsForAllLocations.boxoriginYs[locations] = y;
				F.FullSummedPtoQAtEpsilons = FLutil.new2dArray(
						F.boxSizes.length, V.Qs.length, 0.0f);
				F.FullMeanTau = FLutil.new2dArray(F.boxSizes.length,
						V.Qs.length, 0.0f);
				F.FullAlphaArray = FLutil.new2dArray(F.boxSizes.length,
						V.Qs.length, 0.0f);
				F.FullFArray = FLutil.new2dArray(F.boxSizes.length,
						V.Qs.length, 0.0f);

			}
			// //////////////////////////////////////////////////////
			// ///// scan for box counting data and /////////////
			// //// store it in global arrays ////////////
			// //////////////////////////////////////////////////////
			AllCounts = doBoxCounts(issub, ipk, x, y, rSLIDINGLACUNARITY,
					slidex, slidey, locations, thissubsample, AllCounts, F, V);

			// win = WindowManager.getCurrentWindow();
			// //debug(55);
			// //////////////////////////////////////////////////////
			// get statistics from arrays filled by box counts ////
			// //////////////////////////////////////////////////////
			FracStats RegressionStatsAtThisLocation = null;
			if (V.cumulative && V.isRandomsquares() && V.dosubs)
				;

			else
			// if (rSLIDINGLACUNARITY) {

			// PrintSLIDINGLACUNARITY(locations, ThisRectangleSubScan,
			// thisslice, V.GraphLac, F, V);

			// } else

			{

				float[][] D = null;

				if (true)
					if (V.GraphAnyQs() || V.isMF())
					{

						D = FinishQslopesForAllEpsilonAtLocation(
								F.FullSummedPtoQAtEpsilons, F.FullMeanTau,
								F.FullAlphaArray, F.FullFArray, locations, F, V);
						xDqs[locations] = D[0];
						Dqs[locations] = D[1];
						Taus[locations] = D[2];
						Alphas[locations] = D[3];
						Fs[locations] = D[4];

					}

				RegressionStatsAtThisLocation = new FracStats(
						AllCounts[locations],
						// epsilon[locations]
						F.boxSizeOverMaxWidth, F.boxSizes.length);

				AvgsForAllLocations.dfs[locations] = RegressionStatsAtThisLocation.df;

				AvgsForAllLocations.yintercepts[locations] = RegressionStatsAtThisLocation.FractalStatsYint;

				AvgsForAllLocations.InverseOfPrefactors[locations] = RegressionStatsAtThisLocation.FractalStatsF;

				// /////////////////////////////////////////////////////////
				FLstatistics EmptyStats = new FLstatistics(
						F.NumberOfEmptyBoxesAtLocationAndEpsilon[locations],
						F.boxSizes.length);

				AvgsForAllLocations.CVForNumberOfEmptyBoxes[locations] = EmptyStats.coefVar;

				FLstatistics StatsAtThisLocation = new FLstatistics(
						AllCounts[locations], F.boxSizes.length);

				AvgsForAllLocations.CVforCounts[locations] = StatsAtThisLocation.coefVar;

				// //////////////////////////////////////////////////////////
				// ///////////////////////////////////////////////////////////
				FLstatistics PixelStats = new FLstatistics(
						F.SqrdCVforPixelsPerBox[locations], F.boxSizes.length);

				AvgsForAllLocations.MeanSqrdCVInPixelsPerBoxAtLocation[locations] = PixelStats.mean;

				PixelStats = new FLstatistics(
						F.ESqrdCVforPixelsPerBox[locations], F.boxSizes.length);

				AvgsForAllLocations.EMeanSqrdCVInPixelsPerBoxAtLocation[locations] = PixelStats.mean;

				PixelStats = new FLstatistics(F.Lacunarity[locations],
						F.boxSizes.length);

				AvgsForAllLocations.MeanLacunarity[locations] = PixelStats.mean;

				PixelStats = new FLstatistics(F.ELacunarity[locations],
						F.boxSizes.length);

				AvgsForAllLocations.EMeanLacunarity[locations] = PixelStats.mean;
				// ///////////////////////////////////////////////////////////
				PixelStats = new FLstatistics(F.binnedPDLAtEpsilon[locations],
						F.boxSizes.length);

				AvgsForAllLocations.binnedCVinProbDensityLacunarity[locations] = PixelStats.coefVar;

				AvgsForAllLocations.binnedMeanProbDensityLacunarity[locations] = PixelStats.mean;

				String ScanDimensions = (V.StartX - V.xOffset) + ","
						+ (V.StartY - V.yOffset) + "--" + (V.EndX - V.xOffset)
						+ "," + (V.EndY - V.yOffset) + " ";
				String ssddd = V.OriginalImageTitle + thisslice + " (" + RoiX
						+ "," + RoiY + ":" + RoiWidth + "x" + RoiHeight + ") "
						+ ScanDimensions + FLEmu.tab;

				// ////////////////////////////////////////////////////////////
				// if(!V.SkipRawData)V.RawDataString=V.RawDataString+RawDataString(
				//
				// ssddd,
				// AvgsForAllLocations.boxoriginXs[locations],
				// AvgsForAllLocations.boxoriginYs[locations],
				// thissubsample,
				// RegressionStatsAtThisLocation,
				// AvgsForAllLocations,
				// locations,
				// F, V);

				Qlines[locations] = PrepareStringsFromThisOrigin(Dqs, xDqs,
						Taus, Alphas, Fs, ssddd,
						AvgsForAllLocations.boxoriginXs[locations],
						AvgsForAllLocations.boxoriginYs[locations],
						thissubsample, RegressionStatsAtThisLocation,
						AvgsForAllLocations, locations, RoiWidth, RoiHeight, F,
						V);
			}
			if (!issub)
			{
				V.Globalxy[locations][0] = V.actualx;
				V.Globalxy[locations][1] = V.getActualy();
			}

		}/* end for locations */

		int[] roimargins = { V.getActualy(), V.actualbot, V.actualrt, V.actualx };

		if (V.cumulative && V.dosubs && V.isRandomsquares())
			;

		else if (V.SLAC)
		{
		}

		else
			finishdata(Qlines, Dqs, xDqs, Taus, Fs, Alphas, thissubsample, x,
					y, ipk, RoiX, RoiY, RoiWidth, RoiHeight, thisslice,
					lastslice, AvgsForAllLocations, circ, issub, suborglobal,
					AllCounts, islastsub, roimargins, isRectangleSubScan,
					ThisRectangleSubScan, F, V);

	}

	private static void ScanPassedImage(String title, ImagePlus img,
			int XOfAreaBeingChecked, int YOfAreaBeingChecked,
			int FullWidthOfAreaBeingChecked, int FullHeightOfAreaBeingChecked,
			int thisslice, int lastslice, boolean DoRandomSquares,
			boolean issub, boolean isRectangleSubScan,
			int ThisRectangleSubScan,

			FLVars V, FLEmu F)
	{
		V.didMassHeadings = false;
		// IJ.showStatus("Preparing "+V.OriginalImageTitle);
		// progress=0;
		ImageProcessor ip = img.getProcessor();

		// /////////////////////////////////////////////////////
		// // open and prepare the files selected ///////
		// /////////////////////////////////////////////////////
		if (ip != null)
		{
			V.originaldrawing = new ImagePlus(
					"oDrawing" + V.OriginalImageTitle, ip);
			V.originaldrawing.setProcessor("oDrawing" + V.OriginalImageTitle,
					ip);
			ip = ready(title, img, issub, F, V);// store this window's name,

			// then run the routine to prepare images.

			if (ip == null)
			{
				V.abort = true;
				System.out.println("error in processing");
				return;
			} else
			// Set this variable for colour coding.
			if (!V.isAbort())
			{
				V.expandeddrawing = new ImagePlus("xDrawing"
						+ V.OriginalImageTitle, ip);
				V.expandeddrawing.setProcessor("xDrawing"
						+ V.OriginalImageTitle, ip);

				// /////////////////////////////////////////////////////
				// //////// If required, set parameters //////////
				// ////////// for doing subareas /////////////
				// /////////////////////////////////////////////////////
				// if (V.dosubs)setupSubs(DoRandomSquares, thisslice);

				// /////////////////////////////////////////////////////
				// //////// If required, ///////
				// //////// analyze the entire roi or image /////
				// /////////////////////////////////////////////////////

				if (V.doglobal)
					ScanImageAtEachLocation(0, true, false, ip,
							XOfAreaBeingChecked, YOfAreaBeingChecked,
							FullWidthOfAreaBeingChecked,
							FullHeightOfAreaBeingChecked, thisslice, lastslice,
							F.RectangleEnclosingForeground.x,
							F.RectangleEnclosingForeground.y, V.SLAC, V.SLIDEX,
							V.SLIDEY, false, isRectangleSubScan,
							ThisRectangleSubScan, F, V);

				// /////////////////////////////////////////////////////
				// //////// Analyze subareas of the roi or //////
				// //////// image if this option is chosen //////
				// /////////////////////////////////////////////////////
				/*
				 * if (V.dosubs)runSubs(ip, thisslice, lastslice,
				 * DoRandomSquares, isRectangleSubScan, ThisRectangleSubScan);
				 */
				// /////////////////////////////////////////////////////
				// Close the image if no coloured graphic is made ////
				// /////////////////////////////////////////////////////

				if (V.showcolours || V.isUseParticleAnalyzer()
						|| V.UseRectangularAnalyzer)
					;
				// if (V.dontclose);
				else
				{
					// win.close();
					if (V.wasAGrab)
						;

				}
				V.wasAGrab = false;
			}

		} else
		{
			// System.out.println("No Image");
			System.out.println("No Image");
		}// end if there is an image
	}
	
	private static ImagePlus openUsingImageIO(String path) {
		ImagePlus imp = null;
		BufferedImage img = null;
		File f = new File(path);
		img = Util.loadImage(path);
		if (img==null) return null;
		if (img.getColorModel().hasAlpha()) {
			int width = img.getWidth();
			int height = img.getHeight();
			BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
			Graphics g = bi.getGraphics();
			g.setColor(Color.white);
			g.fillRect(0,0,width,height);
			g.drawImage(img, 0, 0, null);
			img = bi;
		}
		imp = new ImagePlus(f.getName(), img);
		
		/*FileInfo fi = new FileInfo();
		fi.fileFormat = fi.IMAGEIO;
		fi.fileName = f.getName();
		fi.directory = f.getParent()+File.separator;
		imp.setFileInfo(fi);
		*/
		return imp;
	}
	
	private static ImagePlus openImage(String path) {
		ImagePlus imp;
		//String path = directory+name;
		imp = openUsingImageIO(path);
		if (imp!=null&&imp.getWidth()!=0) 
			return imp; 
		else 
			return null;
	}
	
	private static String ProcessSingleImage(
			boolean DoRandomSquares, boolean isRectangleSubScan, BufferedImage bimage, String imageName,
			FLVars V)
			throws Throwable
	{
		//ArrayList<String> resultData = new ArrayList<String>();
		FLEmu F = new FLEmu();

		

		//ImagePlus img = openImage(files[i].getAbsolutePath());
		V.OriginalImageTitle = imageName; // img.getTitle();
		String title = V.OriginalImageTitle;
		
		ImagePlus img = new ImagePlus(imageName, bimage);

		V.abort = CheckBW(img, V);

		if (!V.abort)
		{
			int lastslice = img.getStackSize();

			Roi[] RoisFromParticleAnalyzer = null;

			//for (int thisslice = 1; thisslice <= lastslice;)
			
			int thisslice = 1;
			img.setSlice(thisslice);
			V.OriginalImageTitle = title + thisslice;
			V.data = "";
			V.RawDataString = "";
			if (thisslice == 1)
			{
				RoisFromParticleAnalyzer = new Roi[1];
				RoisFromParticleAnalyzer[0] = new Roi(0, 0,
						img.getWidth(), img.getHeight());
			}

			ScanPassedImage(V.OriginalImageTitle, img, 0, 0,
					img.getWidth(), img.getHeight(), thisslice,
					lastslice, DoRandomSquares, false, false, 0, V, F);

			// WriteScanData(ScanType);

			// System.out.println("Processed file (" + (i + 1) + "/" +
			// files.length + ") : " + files[i].getName());

			// ZXC //
			// System.out.println(F.V.data);
			//resultData.add(V.data);
			return V.data;
			
		}
		else
		{
			return null;
		}
	}

	private static ArrayList<String> OpenAndScanFilesWithoutShowingThem(
			boolean DoRandomSquares, boolean isRectangleSubScan, File[] files,
			FLVars V, int resizeW, int resizeH, ImagePreprocessor preProc)
			throws Throwable
	{
		ArrayList<String> resultData = new ArrayList<String>();
		FLEmu F = new FLEmu();

		for (int i = 0; i < files.length; i++)
		{

			ImagePlus img = openImage(files[i].getAbsolutePath());
			V.OriginalImageTitle = img.getTitle();
			String title = V.OriginalImageTitle;

			boolean autoResize = (resizeW > 0) && (resizeH > 0);

			if (autoResize)
			{
				if (img.getWidth() != resizeW || img.getHeight() != resizeH)
				{
					BufferedImage resizedImage = new BufferedImage(resizeW,
							resizeH, BufferedImage.TYPE_3BYTE_BGR);
					Graphics2D g = (Graphics2D) resizedImage.getGraphics();
					g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
							RenderingHints.VALUE_INTERPOLATION_BILINEAR);
					g.drawImage(img.getImage(), 0, 0, resizeW, resizeH, null);
					g.dispose();
					img.setImage(resizedImage);
				}
			}

			V.abort = CheckBW(img, V);

			if (!V.abort)
			{
				int lastslice = img.getStackSize();

				Roi[] RoisFromParticleAnalyzer = null;

				for (int thisslice = 1; thisslice <= lastslice; ++thisslice)
				{
					img.setSlice(thisslice);
					V.OriginalImageTitle = title + thisslice;
					V.data = "";
					V.RawDataString = "";
					if (thisslice == 1)
					{
						RoisFromParticleAnalyzer = new Roi[1];
						RoisFromParticleAnalyzer[0] = new Roi(0, 0,
								img.getWidth(), img.getHeight());
					}

					ScanPassedImage(V.OriginalImageTitle, img, 0, 0,
							img.getWidth(), img.getHeight(), thisslice,
							lastslice, DoRandomSquares, false, false, 0, V, F);

					// WriteScanData(ScanType);

					// System.out.println("Processed file (" + (i + 1) + "/" +
					// files.length + ") : " + files[i].getName());

					// ZXC //
					// System.out.println(F.V.data);
					resultData.add(V.data);

				}
			}
		}// end loop through list of images

		return resultData;
	}

	public static String doFile(File file, ImagePreprocessor preprocessor,
			int resizeW, int resizeH)
	{
		File[] files = new File[1];
		files[0] = file;
		ArrayList<String> fileData;

		FLVars V = new FLVars();

		try
		{
			fileData = OpenAndScanFilesWithoutShowingThem(V.isRandomsquares(),
					false, files, V, resizeW, resizeH, preprocessor);
		} catch (Throwable e)
		{
			System.out.println("Exception in processing " + file.getName()
					+ " (" + e.getMessage() + ")");
			e.printStackTrace();
			return null;
		}
		return fileData.get(0);
	}
	
	public static String doImage(BufferedImage image, String imageName)
	{
		//ArrayList<String> fileData;

		FLVars V = new FLVars();

		try
		{
			//fileData = OpenAndScanFilesWithoutShowingThem(V.isRandomsquares(),
				//	false, files, V, resizeW, resizeH, preprocessor);
			String imageData = ProcessSingleImage(V.isRandomsquares(), false, image,
					imageName, V);
			return imageData;
		} catch (Throwable e)
		{
			System.out.println("Exception in processing " + imageName
					+ " (" + e.getMessage() + ")");
			e.printStackTrace();
			return null;
		}
		//return fileData.get(0);
	}

	@SuppressWarnings("unused")
	public
	static HashMap<String, String> processFiles(String[] fileNames,
			String outputFile, ImagePreprocessor preprocessor, int resizeW,
			int resizeH)
	{
		if (fileNames == null)
		{
			System.out.println("Empty file list, aborting");
			return null;
		}

		ArrayList<File> foundFiles = new ArrayList<File>();
		for (String name : fileNames)
		{
			File f = new File(name);
			if (f.exists())
			{
				foundFiles.add(f);

				if (maxEntries > 0 && foundFiles.size() >= maxEntries)
					break;
			}
		}

		if (foundFiles.size() == 0)
		{
			System.out.println("None of the files found! Aborting!");
			return null;
		}

		int cores = Runtime.getRuntime().availableProcessors();

		if (foundFiles.size() < cores)
			cores = foundFiles.size();

		long startTime = System.currentTimeMillis();

		System.out.println("Detected CPU cores : " + cores);
		if (cpuCoresOverride > 0)
		{
			cores = cpuCoresOverride;
			System.out.println("CPU cores overriden to " + cores);
		}

		HashMap<String, String> fileData = new HashMap<String, String>();

		// PHASE 2
		try
		{
			File[] files = new File[foundFiles.size()];
			int i = 0;
			for (File f : foundFiles)
				files[i++] = f;

			PrintWriter pw = null;

			if (outputFile != null)
				pw = new PrintWriter(new File(outputFile));

			// ArrayList<String> fileData = new ArrayList<String>();

			// SynchronousQueue<File> fileQueue = new SynchronousQueue<File>();
			BlockingDeque<File> fileQueue = new LinkedBlockingDeque<File>();
			ConcurrentHashMap<String, String> results = new ConcurrentHashMap<String, String>();

			for (File file : files)
				fileQueue.add(file);

			int fileCount = fileQueue.size();
			AtomicInteger atomCounter = new AtomicInteger();

			Worker[] workers = new Worker[cores];
			for (i = 0; i < cores; ++i)
				workers[i] = new Worker(fileQueue, results, atomCounter,
						preprocessor, resizeW, resizeH);

			for (i = 0; i < cores; ++i)
			{
				System.out.println("THREAD " + (i + 1) + " OF " + cores);
				Thread t = new Thread(workers[i]);
				t.start();
				Thread.sleep(10);
			}

			int lastCount = 0;

			while (atomCounter.get() < fileCount)
			{
				Thread.sleep(100);
				int ccount = atomCounter.get();
				if (ccount > lastCount)
					System.out.println("PROGRESS : " + ccount + " / "
							+ fileCount);
				lastCount = ccount;
			}

			System.out.println("All entries processed.");
			/*
			 * for(File file : files) { //fileData.add(doFile(file, F, fl));
			 * System.out.println("Doing file : " + file.getName()); String
			 * result = doFile(file, F, fl); fileData.put(file.getName(),
			 * result); }
			 */

			// System.out.println("Data output begin");
			for (Entry<String, String> entry : results.entrySet())
			{
				// System.out.println("Key = " + entry.getKey());
				String finalData = entry.getValue().replace('\t', ';');
				DecimalFormatSymbols dfs = new DecimalFormatSymbols();
				if (dfs.getDecimalSeparator() != DECIMAL_SEPARATOR)
					finalData = finalData.replace(dfs.getDecimalSeparator(),
							DECIMAL_SEPARATOR);

				entry.setValue(finalData);

				// System.out.println(finalData);

				if (pw != null)
					pw.println(finalData);
			}
			// System.out.println("Data output end");

			if (pw != null)
				pw.close();
		} catch (Throwable e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		int secondsElapsed = (int) ((System.currentTimeMillis() - startTime) / 1000);
		int minutes = secondsElapsed / 60;
		if (minutes > 0)
			secondsElapsed = secondsElapsed % 60;

		if (minutes > 0)
			System.out.println("Time elapsed : " + minutes + " minutes and "
					+ secondsElapsed + " seconds");
		else
			System.out.println("Time elapsed : " + secondsElapsed + " seconds");

		// System.out.println("Done? :O");
		return fileData;
	}

	private static void parseFiles(File dir, ArrayList<String> list)
	{
		File[] entries = dir.listFiles();
		
		for(File f : entries)
		{
			if(f.isDirectory()) continue;
			String lname = f.getName().toLowerCase();
			if(lname.endsWith(".jpg") || lname.endsWith(".jpeg") ||
					lname.endsWith(".png") || lname.endsWith(".bmp") ||
					lname.endsWith(".gif"))
			{
				list.add(f.getAbsolutePath());
			}
		}
		
		for(File f : entries)
		{
			if(!f.isDirectory()) continue;
			parseFiles(f, list);
		}
	}

	public static String[] getImageList(String string)
	{
		File dir = new File(string);
		if(!dir.exists()) return null;
		if(!dir.isDirectory()) return null;
		
		ArrayList<String> files = new ArrayList<String>();
		parseFiles(dir, files);
		String[] list = new String[files.size()];
		files.toArray(list);
		return list;
	}
}
